#pragma once

namespace MediaFoundationSamples
{

    //////////////////////////////////////////////////////////////////////////
    //  AsyncCallback [template]
    //
    //  Description: 
    //  Helper class that routes IMFAsyncCallback::Invoke calls to a class
    //  method on the parent class.
    //
    //  Usage:
    //  Add this class as a member variable. In the parent class constructor,
    //  initialize the AsyncCallback class like this:
    //      m_cb(this, &CYourClass::OnInvoke)
    //  where
    //      m_cb       = AsyncCallback object
    //      CYourClass = parent class
    //      OnInvoke   = Method in the parent class to receive Invoke calls.
    //
    //  The parent's OnInvoke method (you can name it anything you like) must
    //  have a signature that matches the InvokeFn typedef below.
    //////////////////////////////////////////////////////////////////////////

    // T: Type of the parent object
    template<class T>
    class AsyncCallback : public IMFAsyncCallback
    {
    public: 
        typedef HRESULT (T::*InvokeFn)(IMFAsyncResult *pAsyncResult);

        AsyncCallback(T *pParent, InvokeFn fn) : m_pParent(pParent), m_pInvokeFn(fn)
        {
        }

        // IUnknown
        STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
        {
            if (!ppv)
            {
                return E_POINTER;
            }
            if (iid == __uuidof(IUnknown))
            {
                *ppv = static_cast<IUnknown*>(static_cast<IMFAsyncCallback*>(this));
            }
            else if (iid == __uuidof(IMFAsyncCallback))
            {
                *ppv = static_cast<IMFAsyncCallback*>(this);
            }
            else
            {
                *ppv = NULL;
                return E_NOINTERFACE;
            }
            AddRef();
            return S_OK;
        }
        STDMETHODIMP_(ULONG) AddRef() { 
            // Delegate to parent class.
            return m_pParent->AddRef(); 
        }
        STDMETHODIMP_(ULONG) Release() { 
            // Delegate to parent class.
            return m_pParent->Release(); 
        }


        // IMFAsyncCallback methods
        STDMETHODIMP GetParameters(DWORD*, DWORD*)
        {
            // Implementation of this method is optional.
            return E_NOTIMPL;
        }

        STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult)
        {
            return (m_pParent->*m_pInvokeFn)(pAsyncResult);
        }

        T *m_pParent;
        InvokeFn m_pInvokeFn;
    };

}; // namespace MediaFoundationSamples